<!DOCTYPE html>

<html lang="en">

<head>
    <title>WebGL 2 Samples - texture_immutable</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <script src="utility.js"></script>
    <script src="third-party/gl-matrix-min.js"></script>
    <script src="third-party/noise3D.js"></script>

</head>

<body>
    <div id="info">WebGL 2 Samples - texture_immutable</div>
    <p id="description">Immutable texture refers to the texture's allocation, not the content of the texture. Therefore, new pixel data can be uploaded, but the size of the texture storage can't change.</p>

    <!-- WebGL 2 shaders -->
    <script id="vs" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0
        #define TEXCOORD_LOCATION 4

        precision highp float;
        precision highp int;

        uniform mat4 MVP;

        layout(location = POSITION_LOCATION) in vec2 position;
        layout(location = TEXCOORD_LOCATION) in vec2 texcoord;

        out vec2 v_st;

        void main()
        {
            v_st = texcoord;
            gl_Position = MVP * vec4(position, 0.0, 1.0);
        }
    </script>

    <script id="fs" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        uniform sampler2D diffuse;

        in vec2 v_st;

        out vec4 color;

        void main()
        {
            color = texture(diffuse, v_st);
        }
    </script>

    <script id="vs-3d" type="x-shader/x-vertex">
        #version 300 es
        #define POSITION_LOCATION 0
        #define TEXCOORD_LOCATION 4

        precision highp float;
        precision highp int;

        layout(location = POSITION_LOCATION) in vec2 position;
        layout(location = TEXCOORD_LOCATION) in vec2 in_texcoord;

        // Output 3D texture coordinate after transformation
        out vec3 v_texcoord;

        void main()
        {
            // Multiply the texture coordinate by the transformation
            // matrix to place it into 3D space
            v_texcoord = (mat4(1.0) * vec4(in_texcoord - vec2(0.5, 0.5), 0.5, 1.0)).stp;
            gl_Position = vec4(position, 0.0, 1.0);
        }

    </script>

    <script id="fs-3d" type="x-shader/x-fragment">
        #version 300 es

        precision highp float;
        precision highp int;
        precision highp sampler3D;

        uniform sampler3D diffuse;

        in vec3 v_texcoord;

        out vec4 color;

        void main()
        {
            color = texture(diffuse, v_texcoord);
        }
    </script>

    <script>
    (function () {
        'use strict';

        var canvas = document.createElement('canvas');
        canvas.width = Math.min(window.innerWidth, window.innerHeight);
        canvas.height = canvas.width;
        document.body.appendChild(canvas);

        var gl = canvas.getContext( 'webgl2', { antialias: false } );
        var isWebGL2 = !!gl;
        if(!isWebGL2) {
            document.getElementById('info').innerHTML = 'WebGL 2 is not available.  See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
            return;
        }

        var Corners = {
            LEFT: 0,
            RIGHT: 1,
            MAX: 2
        };

        var viewports = new Array(Corners.MAX);

        viewports[Corners.LEFT] = {
            x: 0,
            y: canvas.height / 4,
            z: canvas.width / 2,
            w: canvas.height / 2
        };

        viewports[Corners.RIGHT] = {
            x: canvas.width / 2,
            y: canvas.height / 4,
            z: canvas.width / 2,
            w: canvas.height / 2
        };

        // -- Init program
        var program = createProgram(gl, getShaderSource('vs'), getShaderSource('fs'));
        var mvpLocation = gl.getUniformLocation(program, 'MVP');
        var diffuseLocation = gl.getUniformLocation(program, 'diffuse');

        var program3D = createProgram(gl, getShaderSource('vs-3d'), getShaderSource('fs-3d'));
        var diffuseLocation3D = gl.getUniformLocation(program3D, 'diffuse');

        // -- Init buffers: vec2 Position, vec2 Texcoord
        var positions = new Float32Array([
            -1.0, -1.0,
             1.0, -1.0,
             1.0,  1.0,
             1.0,  1.0,
            -1.0,  1.0,
            -1.0, -1.0
        ]);
        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var texCoords = new Float32Array([
            0.0, 1.0,
            1.0, 1.0,
            1.0, 0.0,
            1.0, 0.0,
            0.0, 0.0,
            0.0, 1.0
        ]);
        var vertexTexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        // -- Init VertexArray
        var vertexArray = gl.createVertexArray();
        gl.bindVertexArray(vertexArray);

        var vertexPosLocation = 0; // set with GLSL layout qualifier
        gl.enableVertexAttribArray(vertexPosLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        var vertexTexLocation = 4; // set with GLSL layout qualifier
        gl.enableVertexAttribArray(vertexTexLocation);
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexBuffer);
        gl.vertexAttribPointer(vertexTexLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);

        gl.bindVertexArray(null);

        var texture3D = create3DTexture();

        var imageUrl = '../assets/img/Di-3d.png';
        loadImage(imageUrl, function(image) {
            // -- Init 2D Texture
            var texture2D = gl.createTexture();
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture2D);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

            // -- Allocate storage for the texture
            gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB8, 512, 512);
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGB, gl.UNSIGNED_BYTE, image);

            // -- Render
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT);

            gl.bindVertexArray(vertexArray);

            var matrix = new Float32Array([
                1.0, 0.0, 0.0, 0.0,
                0.0, 1.0, 0.0, 0.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0
            ]);

            // Immutable 2D texture
            gl.useProgram(program);
            gl.uniformMatrix4fv(mvpLocation, false, matrix);
            gl.uniform1i(diffuseLocation, 0);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, texture2D);
            gl.viewport(viewports[Corners.LEFT].x, viewports[Corners.LEFT].y, viewports[Corners.LEFT].z, viewports[Corners.LEFT].w);
            gl.drawArrays(gl.TRIANGLES, 0, 6);

            // Immutable 3D texture
            gl.useProgram(program3D);
            gl.uniform1i(diffuseLocation3D, 1);

            gl.activeTexture(gl.TEXTURE1);
            gl.bindTexture(gl.TEXTURE_3D, texture3D);
            gl.viewport(viewports[Corners.RIGHT].x, viewports[Corners.RIGHT].y, viewports[Corners.RIGHT].z, viewports[Corners.RIGHT].w);
            gl.drawArrays(gl.TRIANGLES, 0, 6);

            // Delete WebGL resources
            gl.deleteBuffer(vertexPosBuffer);
            gl.deleteBuffer(vertexTexBuffer);
            gl.deleteTexture(texture2D);
            gl.deleteTexture(texture3D);
            gl.deleteProgram(program);
            gl.deleteProgram(program3D);
            gl.deleteVertexArray(vertexArray);
        });

        function create3DTexture() {

            // Note By @kenrussel: The sample was changed from R32F to R8 for best portability.
            // not all devices can render to floating-point textures
            // (and, further, this functionality is in a WebGL extension: EXT_color_buffer_float),
            // and renderability is a requirement for generating mipmaps.

            var SIZE = 32;
            var data = new Uint8Array(SIZE * SIZE * SIZE);
            for (var k = 0; k < SIZE; ++k) {
                for (var j = 0; j < SIZE; ++j) {
                    for (var i = 0; i < SIZE; ++i) {
                        data[i + j * SIZE + k * SIZE * SIZE] = snoise([i, j, k]) * 256;
                    }
                }
            }

            var texture3D = gl.createTexture();
            gl.activeTexture(gl.TEXTURE1);
            gl.bindTexture(gl.TEXTURE_3D, texture3D);
            gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_BASE_LEVEL, 0);
            gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAX_LEVEL, Math.log2(SIZE));
            gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
            gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

            gl.texStorage3D(
                gl.TEXTURE_3D,   // target
                Math.log2(SIZE), // levels
                gl.R8,           // internalformat
                SIZE,            // width
                SIZE,            // height
                SIZE             // depth
                );

            gl.texSubImage3D(
                gl.TEXTURE_3D,  // target
                0,              // level
                0,
                0,
                0,
                SIZE,           // width
                SIZE,           // height
                SIZE,           // depth
                gl.RED,         // format
                gl.UNSIGNED_BYTE,       // type
                data
                );

            gl.generateMipmap(gl.TEXTURE_3D);

            return texture3D;
        }
    })();
    </script>
    <div id="highlightedLines"  style="display: none">#L201-L212</div>

</body>

</html>
